//
// Created by lugang on 2023/12/19.
//

#ifndef IPC_VECTORBUFFER_H
#define IPC_VECTORBUFFER_H
#include <iostream>
#include <vector>
#include <cassert>
#include <cstring>

class Buffer
{
public:

    static const size_t DEFAULT_CAPACITY = 1024;

    explicit Buffer(size_t initialSize = DEFAULT_CAPACITY)
            : buffer_(initialSize),
              read_index(0),
              write_index(0)
    {
        assert(ReadableBytes() == 0);
        assert(WritableBytes() == initialSize);
    }


    void swap(Buffer& rhs)
    {
        buffer_.swap(rhs.buffer_);
        std::swap(read_index, rhs.read_index);
        std::swap(write_index, rhs.write_index);
    }

    size_t ReadableBytes() const
    { return write_index - read_index; }

    size_t WritableBytes() const
    { return buffer_.size() - write_index; }

    const char* Peek() const
    { return Begin() + read_index; }

    void Retrieve(size_t len)
    {
        assert(len <= ReadableBytes());
        if (len < ReadableBytes())
        {
            read_index += len;
        }
        else
        {
            retrieveAll();
        }
    }

    void RetrieveInt64()
    {
        Retrieve(sizeof(int64_t));
    }

    void RetrieveInt32()
    {
        Retrieve(sizeof(int32_t));
    }

    void RetrieveInt16()
    {
        Retrieve(sizeof(int16_t));
    }

    void RetrieveInt8()
    {
        Retrieve(sizeof(int8_t));
    }

    void retrieveAll()
    {
        read_index = 0;
        write_index = 0;
    }

    std::string RetrieveAllAsString()
    {
        return RetrieveAsString(ReadableBytes());
    }

    std::string RetrieveAsString(size_t len)
    {
        assert(len <= ReadableBytes());
        std::string result(Peek(), len);
        Retrieve(len);
        return result;
    }
    

    void Append(const char*  data, size_t len)
    {
        MakeSureWritableBytes(len);
        std::copy(data, data+len, BeginWrite());
        hasWritten(len);
    }

    void Append(const void* /*restrict*/ data, size_t len)
    {
        Append(static_cast<const char *>(data), len);
    }

    void MakeSureWritableBytes(size_t len)
    {
        if (WritableBytes() < len)
        {
            makeSpace(len);
        }
        assert(WritableBytes() >= len);
    }

    char* BeginWrite()
    { return Begin() + write_index; }

    const char* beginWrite() const
    { return Begin() + write_index; }

    void hasWritten(size_t len)
    {
        assert(len <= WritableBytes());
        write_index += len;
    }

    void appendInt64(int64_t x)
    {
        int64_t be64 = HostToNetwork64(x);
        Append(&be64, sizeof be64);
    }

    void appendInt32(int32_t x)
    {
        int32_t be32 = HostToNetwork32(x);
        Append(&be32, sizeof be32);
    }

    void appendInt16(int16_t x)
    {
        int16_t be16 = HostToNetwork16(x);
        Append(&be16, sizeof be16);
    }

    void appendInt8(int8_t x)
    {
        Append(&x, sizeof x);
    }

    int64_t readInt64()
    {
        int64_t result = peekInt64();
        RetrieveInt64();
        return result;
    }

    int32_t readInt32()
    {
        int32_t result = peekInt32();
        RetrieveInt32();
        return result;
    }

    int16_t readInt16()
    {
        int16_t result = peekInt16();
        RetrieveInt16();
        return result;
    }

    int8_t readInt8()
    {
        int8_t result = peekInt8();
        RetrieveInt8();
        return result;
    }
    int64_t peekInt64() const
    {
        assert(ReadableBytes() >= sizeof(int64_t));
        int64_t be64 = 0;
        ::memcpy(&be64, Peek(), sizeof be64);
        return NetworkToHost64(be64);
    }
    int32_t peekInt32() const
    {
        assert(ReadableBytes() >= sizeof(int32_t));
        int32_t be32 = 0;
        ::memcpy(&be32, Peek(), sizeof be32);
        return NetworkToHost32(be32);
    }
    int16_t peekInt16() const
    {
        assert(ReadableBytes() >= sizeof(int16_t));
        int16_t be16 = 0;
        ::memcpy(&be16, Peek(), sizeof be16);
        return NetworkToHost16(be16);
    }

    int8_t peekInt8() const
    {
        assert(ReadableBytes() >= sizeof(int8_t));
        int8_t x = *Peek();
        return x;
    }

    void shrink(size_t reserve)
    {
        Buffer other;
        other.MakeSureWritableBytes(ReadableBytes() + reserve);
        other.Append(Peek(), static_cast<int>(ReadableBytes()));
        swap(other);
    }

    size_t internalCapacity() const
    {
        return buffer_.capacity();
    }
    inline uint64_t HostToNetwork64(uint64_t host64) const
    {
        return htobe64(host64);
    }

    inline uint32_t HostToNetwork32(uint32_t host32) const
    {
        return htobe32(host32);
    }

    inline uint16_t HostToNetwork16(uint16_t host16) const
    {
        return htobe16(host16);
    }

    inline uint64_t NetworkToHost64(uint64_t net64) const
    {
        return be64toh(net64);
    }

    inline uint32_t NetworkToHost32(uint32_t net32) const
    {
        return be32toh(net32);
    }

    inline uint16_t NetworkToHost16(uint16_t net16) const
    {
        return be16toh(net16);
    }

private:

    char* Begin()
    { return &*buffer_.begin(); }

    const char* Begin() const
    { return &*buffer_.begin(); }

    void makeSpace(size_t len)
    {
        if (WritableBytes() < len)
        {
            buffer_.resize(write_index + len);
        }
    }

private:
    std::vector<char> buffer_;
    size_t read_index;
    size_t write_index;
};
#endif //IPC_VECTORBUFFER_H
